#include "grid.hpp"

Grid::Grid(const Grid & src, const Box2i & region)
{
	Resize(region.size.x, region.size.y);

	int sc = region.off.x, sr = region.off.y;

	for (int r = 0; r < rows && sr < src.rows; ++r, ++sr)
	{
		for (int c = 0; c < cols && sc < src.cols; ++c, ++sc)
		{
			Put(c, r, src.Get(sc, sr));
		}
	}
}

void Grid::Resize(int newcols, int newrows)
{
	if (TileCt() < newcols * newrows)
	{
		tiles.resize(newcols * newrows, 0);
	}

	int dcols = newcols - cols;

	if (dcols < 0)
	{
		for (int r = 1; r < newrows; ++r)
		{
			for (int c = 0; c < newcols; ++c)
			{
				int i = r*newcols + c;
				if (r >= rows)
					tiles[i] = 0;
				else
					tiles[i] = tiles[i - (r*dcols)];
			}
		}
	}
	else if (dcols > 0) // data loss if we do the above in this case
	{
		for (int r = newrows - 1; r > -1; --r)
		{
			for (int c = newcols - 1; c > -1; --c)
			{
				int i = r*newcols + c;
				if (c >= cols || r >= rows)
					tiles[i] = 0;
				else
					tiles[i] = tiles[i - (r*dcols)];
			}
		}
	}

	if (TileCt() > newcols * newrows)
	{
		tiles.resize(newcols * newrows);
	}

	cols = newcols;
	rows = newrows;
}

#include <algorithm>
#include <cstdarg>
#include <cstdio>
#include <wchar.h>	// cwchar consistency not guaranteed

#ifdef WIN32
#define VSWPrintf vsnwprintf
#else
#define VSWPrintf vswprintf
#endif

int Grid::Printf(int c, int r, const Tile *fmt, ...)
{
	int maxct = TileCt() - TileN(c, r) + 1;
	if (maxct < 1)
		return maxct;

	Tile buf[256];
	maxct = std::min(256, maxct);

	std::va_list args;
	va_start(args, fmt);
	int cct = VSWPrintf(buf, maxct, fmt, args);
	va_end(args);

	if (0 < cct)
		tiles.replace(TileN(c, r), cct, buf, cct);

	return cct;
}

int Grid::Replace(Tile srch, Tile repl)
{
	int count = 0;

	for (size_t i = 0; i < tiles.size(); ++i)
	{
		if (tiles[i] == srch)
		{
			tiles[i] = repl;
			++count;
		}
	}

	return count;
}

int Grid::GPut(int c, int r, const Grid & src)
{
	int count = 0;
	int c0 = c;

	for (int sr = 0; sr < src.rows && r < rows; ++sr, ++r)
	{
		if (r < 0)
			continue;

		for (int sc = 0; sc < src.cols && c < cols; ++sc, ++c)
		{
			if (c < 0)
				continue;

			Put(c, r, src.Get(sc, sr));
			++count;
		}
		c = c0;	
	}
	
	return count;
}

int Grid::GComp(int c, int r, const Grid & grid)
{
	if (c < 0 || r < 0)
		return -1;

	int c0 = c;
	int gr;

	for (gr = 0; gr < grid.rows && r < rows; ++gr, ++r)
	{
		int gc;
		for (gc = 0; gc < grid.cols && c < cols; ++gc, ++c)
		{
			Tile diff = Get(c, r) - grid.Get(gc, gr);
			if (diff)
				return diff;
		}
		
		if (gc < grid.cols)
		{
			return -1;
		}

		c = c0;
	}

	return (gr < grid.rows) ? -1 : 0;
}

int Grid::GReplace(const Grid & srch, const Grid & repl)
{
	int count = 0;

	for (int r = 0; r + srch.rows <= rows; ++r)
	{
		for (int c = 0; c + srch.cols <= cols; ++c)
		{
			if (GComp(c, r, srch) == 0)
			{
				count += GPut(c, r, repl);
				c += repl.cols - 1;
			}
		}
	}

	return count;
}
